//-
// ==========================================================================
// Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All 
// rights reserved.
//
// The coded instructions, statements, computer programs, and/or related 
// material (collectively the "Data") in these files contain unpublished 
// information proprietary to Autodesk, Inc. ("Autodesk") and/or its 
// licensors, which is protected by U.S. and Canadian federal copyright 
// law and by international treaties.
//
// The Data is provided for use exclusively by You. You have the right 
// to use, modify, and incorporate this Data into other products for 
// purposes authorized by the Autodesk software license agreement, 
// without fee.
//
// The copyright notices in the Software and this entire statement, 
// including the above license grant, this restriction and the 
// following disclaimer, must be included in all copies of the 
// Software, in whole or in part, and all derivative works of 
// the Software, unless such copies or derivative works are solely 
// in the form of machine-executable object code generated by a 
// source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. 
// AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED 
// WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF 
// NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 
// PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR 
// TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS 
// BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, 
// DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK 
// AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY 
// OR PROBABILITY OF SUCH DAMAGES.
//
// ==========================================================================
//+
#if defined(D3D9_SUPPORTED)

#pragma warning (disable:4239)
#include <stdio.h>

#include <D3DResourceManager.h>

#include <maya/MGlobal.h>
#include <maya/MString.h>
#include <maya/MFnCamera.h>
#include <maya/MAngle.h>
#include <maya/MPoint.h>
#include <maya/MVector.h>
#include <maya/MItDag.h>
#include <maya/MMatrix.h>
#include <maya/MDagPath.h>
#include <maya/MFnDagNode.h>
#include <maya/MBoundingBox.h>
#include <maya/MNodeMessage.h> // For monitor geometry list
#include <maya/MPlug.h>
#include <maya/MPlugArray.h>
#include <maya/MFnLight.h>
#include <maya/MFnSpotLight.h>
#include <maya/MColor.h>
#include <maya/MFloatMatrix.h>

//////////////////////////////////////////////////////////////////////////////////////
//
// Monitors on Maya scene graph
//

// Handle node dirty changes
void geometryDirtyCallback( void* clientData )
{
	GeometryItem *item = (GeometryItem *)clientData;
	if (item)
	{
		MMessage::removeCallback( item->m_objectDirtyMonitor );	
		item->m_objectDirtyMonitor = 0;
		item->m_objectPath = MDagPath(); // Assign in valid dag path to mark as "bad"
	}
}

// Handle node attr change 
void geomteryChangedCallback( MNodeMessage::AttributeMessage msg, MPlug & plug, MPlug & otherPlug, void* clientData)
{
	GeometryItem *item = (GeometryItem *)clientData;
	if (item)
	{
		MMessage::removeCallback( item->m_objectChangeMonitor );	
		item->m_objectChangeMonitor = 0;
		item->m_objectPath = MDagPath(); // Assign in valid dag path to mark as "bad"
	}
}
void textureChangedCallback( MNodeMessage::AttributeMessage msg, MPlug & plug, MPlug & otherPlug, void* clientData)
{
	TextureItem *item = (TextureItem *)clientData;
	if (item)
	{
		MMessage::removeCallback( item->m_objectChangeMonitor );	
		item->m_objectChangeMonitor = 0;
		item->m_mayaNode = MObject::kNullObj; // Assign in valid dag path to mark as "bad"
	}
}

// Handle node delete
void geometryDeleteCallback( MObject &node,
						MDGModifier& modifier,
						void* clientData )
{
	GeometryItem *item = (GeometryItem *)clientData;
	if (item)
	{
		MMessage::removeCallback( item->m_objectDeleteMonitor );	
		item->m_objectDeleteMonitor = 0;
		item->m_objectPath = MDagPath(); // Assign in valid dag path to mark as "bad"
	}
}

void textureDeleteCallback( MObject &node,
						MDGModifier& modifier,
						void* clientData )
{
	TextureItem *item = (TextureItem *)clientData;
	if (item)
	{
		MMessage::removeCallback( item->m_objectDeleteMonitor );	
		item->m_objectDeleteMonitor = 0;
		item->m_mayaNode = MObject::kNullObj; // Assign in valid dag path to mark as "bad"
	}
}

///////////////////////////////

D3DResourceManager::D3DResourceManager()
{
	// All lights are off
	m_numberLightsEnabled = 0;

	initializeDefaultCamera();
}

/* virtual */
D3DResourceManager::~D3DResourceManager()
{
	const bool onlyInvalidItems = false;
	clearResources(onlyInvalidItems, true);
}

void					
D3DResourceManager::initializeDefaultCamera()
{
	// Set the default camera being 10 up and 10 back, with Y-up (to match Maya).
    m_camera.m_vEyePt = D3DXVECTOR3( 0.0f, 10.0f, -10.0f );
    m_camera.m_vLookatPt = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
    m_camera.m_vUpVec = D3DXVECTOR3 ( 0.0f, 1.0f, 0.0f );

	// Set up default clip planes, and FOV to match Maya's
	m_camera.m_FieldOfView = 45.0f;
	m_camera.m_nearClip = 0.1f;
	m_camera.m_farClip = 1000.0f;
}

bool					
D3DResourceManager::translateCamera( const MDagPath &cameraPath )
//
// Description:
//		Translate Maya's camera 
//
{
	bool translatedCamera = false;
	if (cameraPath.isValid())
	{
		MStatus status;
		MFnCamera camera (cameraPath, &status);
		if ( !status ) {
			status.perror("MFnCamera constructor");
		}
		else
		{
			translatedCamera = true;

			MPoint eyePoint = camera.eyePoint( MSpace::kWorld );
			MPoint lookAtPt	= camera.centerOfInterestPoint( MSpace::kWorld );
			MVector	upDirection = camera.upDirection ( MSpace::kWorld );
			MFloatMatrix projMatrix = camera.projectionMatrix();

			double horizontalFieldOfView = MAngle( /* camera.verticalFieldOfView() / */ camera.horizontalFieldOfView()
				).asDegrees();
			double nearClippingPlane = camera.nearClippingPlane();
			double farClippingPlane = camera.farClippingPlane();

			// Convert API values to internal native storage.
			//
			m_camera.m_vEyePt = D3DXVECTOR3((float)eyePoint.x, (float)eyePoint.y, (float)eyePoint.z);
			m_camera.m_vLookatPt = D3DXVECTOR3((float)lookAtPt.x, (float)lookAtPt.y, (float)lookAtPt.z);
			m_camera.m_vUpVec = D3DXVECTOR3((float)upDirection.x, (float)upDirection.y, (float)upDirection.z);
			m_camera.m_FieldOfView = (float)horizontalFieldOfView;
			m_camera.m_nearClip = (float)nearClippingPlane;
			m_camera.m_farClip = (float)farClippingPlane;
			m_camera.m_isOrtho = camera.isOrtho();
		}
	}
	else
	{
		initializeDefaultCamera();
	}
	return translatedCamera;
}


void
D3DResourceManager::enableLights( bool val, LPDIRECT3DDEVICE9 D3D )
{
	unsigned i;
	for (i=0; i<m_numberLightsEnabled; i++)
	{
		D3D->LightEnable( i, val);
	}
}

bool D3DResourceManager::cleanupLighting(LPDIRECT3DDEVICE9 D3D)
{
	unsigned int i=0;
	for (i=0; i<m_numberLightsEnabled; i++)
	{
		D3D->LightEnable( i, false);
	}

	LightItemList::const_iterator it, end_it;
	end_it = m_lightItemList.end();
	for (it = m_lightItemList.begin(); it != end_it;  it++)
	{
		LightItem *item = *it;
		if (item)
		{
			delete item;
		}
	}
	m_lightItemList.clear();

	return true;
}

bool D3DResourceManager::setupLighting(LPDIRECT3DDEVICE9 D3D)
//
// Description:
//		Set up lighting / materials.
//
{

	// Set up Maya lights:
	// Nasty, need to scan for lights
	MStatus status;

	MItDag dagIterator( MItDag::kDepthFirst, MFn::kLight, &status );
	MDagPath lightPath;

	for (unsigned int i=0; i<8; i++)
	{
		D3D->LightEnable( i, false);
	}
	m_numberLightsEnabled = 0;

	HRESULT hr;
	for (; !dagIterator.isDone(); dagIterator.next())
	{
		if ( !dagIterator.getPath(lightPath) )
			continue;

		LightItem *lightItem = new LightItem;
		if (!lightItem)
			break;

		lightItem->m_objectDeleteMonitor = NULL;
		lightItem->m_objectDirtyMonitor = NULL;
		ZeroMemory(&(lightItem->m_lightDesc), sizeof(lightItem->m_lightDesc));

		D3DLIGHT9 *d3dLight = &(lightItem->m_lightDesc);
		d3dLight->Range = 10000.0f;
		d3dLight->Falloff = 0.0f;
		d3dLight->Diffuse.a = 1.0f;
		d3dLight->Ambient.a = 1.0f;
		d3dLight->Specular.a = 1.0f;
		d3dLight->Attenuation0 = 0.0f;
		d3dLight->Attenuation1 = 0.0f;
		d3dLight->Attenuation2 = 0.0f;

		// This code doesn't do all of the light attributes, but the
		// general ones are pretty much supported.
		//
		MFnLight    fnLight( lightPath );
		if ( lightPath.hasFn(MFn::kAmbientLight))
		{
			MColor      colorVal = fnLight.color();
			float intensity = fnLight.intensity();

			d3dLight->Type = D3DLIGHT_POINT;	
			d3dLight->Ambient.r = colorVal.r * intensity;
			d3dLight->Ambient.g = colorVal.g * intensity;
			d3dLight->Ambient.b = colorVal.b * intensity;
			d3dLight->Specular.r = 0.0f;
			d3dLight->Specular.g = 0.0f;
			d3dLight->Specular.b = 0.0f;
			d3dLight->Diffuse.r = d3dLight->Ambient.r;
			d3dLight->Diffuse.g = d3dLight->Ambient.g;
			d3dLight->Diffuse.b = d3dLight->Ambient.b;
			d3dLight->Attenuation0 = 1.0;
		}
		else if (lightPath.hasFn(MFn::kDirectionalLight) )
		{
			MColor      colorVal = fnLight.color();
			float intensity = fnLight.intensity();

			d3dLight->Type = D3DLIGHT_DIRECTIONAL;
			d3dLight->Diffuse.r = colorVal.r * intensity;
			d3dLight->Diffuse.g = colorVal.g * intensity;
			d3dLight->Diffuse.b = colorVal.b * intensity;
			d3dLight->Specular.r = d3dLight->Diffuse.r;
			d3dLight->Specular.g = d3dLight->Diffuse.g;
			d3dLight->Specular.b = d3dLight->Diffuse.b;
			d3dLight->Ambient.r = 0.0f;
			d3dLight->Ambient.g = 0.0f;
			d3dLight->Ambient.b = 0.0f;
		}
		else if (lightPath.hasFn(MFn::kPointLight))
		{
			d3dLight->Type = D3DLIGHT_POINT;

			MColor      colorVal = fnLight.color();
			float intensity = fnLight.intensity();

			d3dLight->Type = D3DLIGHT_POINT;
			d3dLight->Diffuse.r = colorVal.r * intensity;
			d3dLight->Diffuse.g = colorVal.g * intensity;
			d3dLight->Diffuse.b = colorVal.b * intensity;
			d3dLight->Specular.r = d3dLight->Diffuse.r;
			d3dLight->Specular.g = d3dLight->Diffuse.g;
			d3dLight->Specular.b = d3dLight->Diffuse.b;
			d3dLight->Ambient.r = 0.0f;
			d3dLight->Ambient.g = 0.0f;
			d3dLight->Ambient.b = 0.0f;
			d3dLight->Falloff = 0;
			// Should set attenuation based on "Decay Rate" attrib on light. TO ADD
			d3dLight->Attenuation0 = 1.0;
			d3dLight->Attenuation1 = 0.0f;
			d3dLight->Attenuation2 = 0.0f;
		}
		else if (lightPath.hasFn(MFn::kSpotLight))
		{
			d3dLight->Type = D3DLIGHT_SPOT;

			MColor      colorVal = fnLight.color();
			float intensity = fnLight.intensity();

			d3dLight->Type = D3DLIGHT_SPOT;
			d3dLight->Diffuse.r = colorVal.r * intensity;
			d3dLight->Diffuse.g = colorVal.g * intensity;
			d3dLight->Diffuse.b = colorVal.b * intensity;
			d3dLight->Specular = d3dLight->Diffuse;
			d3dLight->Ambient.r = 0.0f;
			d3dLight->Ambient.g = 0.0f;
			d3dLight->Ambient.b = 0.0f;

			MFnSpotLight fnSpotLight( lightPath );
			// Differs from OpenGL in that we don't want half the angle (divide by 2) 
			// in our setup.
			d3dLight->Phi = (float) (fnSpotLight.coneAngle() ) + 
							(float) (fnSpotLight.penumbraAngle() );
			d3dLight->Theta = d3dLight->Phi - (float) (fnSpotLight.penumbraAngle() );

			// Should set attenuation based on "Decay Rate" attrib on light. TO ADD
			d3dLight->Attenuation0 = 1.0;
			float dropOffVal = (float) fnSpotLight.dropOff() / 1000.0f;
			d3dLight->Attenuation1 = dropOffVal;
			d3dLight->Falloff = 1.0f;
		}
		else 
		{
			delete lightItem;
			lightItem = NULL;
			continue;
		}

		// Setup common position and direction information.
		if (lightItem && d3dLight)
		{
			MTransformationMatrix worldMatrix = lightPath.inclusiveMatrix();

			MVector translation = worldMatrix.translation( MSpace::kWorld );
			MVector direction( 0.0, 0.0, -1.0 ); 
			direction *= worldMatrix.asMatrix();
			direction.normalize();
			d3dLight->Position.x = (float)translation.x;
			d3dLight->Position.y = (float)translation.y;
			d3dLight->Position.z = (float)translation.z;
			d3dLight->Direction.x = (float)direction.x;
			d3dLight->Direction.y = (float)direction.y;
			d3dLight->Direction.z = (float)direction.z;
		}

		hr = D3D->SetLight( m_numberLightsEnabled, d3dLight);
		if (!SUCCEEDED(hr))
		{
			delete lightItem;
			lightItem = NULL;
			break;
		}
		hr = D3D->LightEnable(m_numberLightsEnabled, TRUE);
		if (!SUCCEEDED(hr))
		{
			delete lightItem;
			lightItem = NULL;
			break;
		}
		m_numberLightsEnabled++;

		// Only allow up to 8 lights to be built for now
		if (m_numberLightsEnabled >= 8)
			break;

		m_lightItemList.push_back( lightItem );
	}

	if (m_numberLightsEnabled == 0)
	{
		// Setup a headlight.
		D3DLIGHT9 Light;
		Light.Type = D3DLIGHT_DIRECTIONAL;
		Light.Specular.r = 1.0f; Light.Specular.g = 1.0f; Light.Specular.b = 1.0f; Light.Specular.a = 1.0f; 
		Light.Diffuse.r = 1.0f; Light.Diffuse.g = 1.0f; Light.Diffuse.b = 1.0f; Light.Diffuse.a = 1.0f; 
		Light.Ambient.r = 0.0f; Light.Ambient.g = 0.0f; Light.Ambient.b = 0.0f; Light.Ambient.a = 0.0f; 
		Light.Range = 100000;
		Light.Falloff = 0;
		Light.Direction = m_camera.m_vLookatPt - m_camera.m_vEyePt;	
		D3D->SetLight( 0, &Light);
		D3D->LightEnable( 0, true);

		// And a backlight.
		D3DLIGHT9 Light2;
		Light2.Type = D3DLIGHT_DIRECTIONAL;
		Light2.Specular.r = 1.0f; Light2.Specular.g = 1.0f; Light2.Specular.b = 1.0f; Light2.Specular.a = 1.0f; 
		Light2.Diffuse.r = 1.0f; Light2.Diffuse.g = 1.0f; Light2.Diffuse.b = 1.0f; Light2.Diffuse.a = 1.0f; 
		Light2.Ambient.r = 0.0f; Light2.Ambient.g = 0.0f; Light2.Ambient.b = 0.0f; Light2.Ambient.a = 0.0f; 
		Light2.Range = 100000;
		Light2.Falloff = 0;
		Light2.Direction = m_camera.m_vEyePt - m_camera.m_vLookatPt;	
		D3D->SetLight( 1, &Light2);
		D3D->LightEnable( 1, true);

		m_numberLightsEnabled = 2;
	}

	D3D->SetRenderState( D3DRS_LIGHTING, TRUE );

	// Make sure specular is on.
	D3D->SetRenderState( D3DRS_SPECULARENABLE, TRUE );
	D3D->SetRenderState(D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL);
	D3D->SetRenderState(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
	// Set lighting to local-viewer
	D3D->SetRenderState(D3DRS_LOCALVIEWER, TRUE);
	// Make sure to auto-normalize normals
	D3D->SetRenderState( D3DRS_NORMALIZENORMALS, TRUE );	

	return true;
}

//
// Get the geometry buffers for this bad boy
//
D3DGeometry* D3DResourceManager::getGeometry( const MDagPath& dagPath, LPDIRECT3DDEVICE9 D3D)
{
	D3DGeometry* Geometry = NULL;

	// Look for a cached mesh ...
	//
	// Check to see if object is in the list, if not added a
	// new item and cache some geometry
	GeometryItem *itemFound = NULL;

	GeometryItemList::const_iterator it, end_it;
	end_it = m_geometryItemList.end();
	for (it = m_geometryItemList.begin(); it != end_it;  it++)
	{
		GeometryItem *item = *it;
		if (item && item->m_objectPath == dagPath)
		{
			itemFound = item;
		}
	}
	// Build a new item, and add it to the list
	if (!itemFound)
	{
		itemFound = new GeometryItem;
		itemFound->m_objectPath = dagPath;
		Geometry = itemFound->m_objectGeometry = new D3DGeometry();
		if (Geometry)
		{
			Geometry->Populate( dagPath, D3D);
		}

		MFnDagNode dagNode(dagPath);
		MObject &obj = (MObject &) dagNode.object();
		itemFound->m_objectDeleteMonitor = 
			MNodeMessage::addNodeAboutToDeleteCallback( obj, geometryDeleteCallback, (void *)itemFound ); // Add callback for node deleted.
		// Don't get attr change messages during playback, so use node dirty also..sigh
		//itemFound->m_objectChangeMonitor = 
		//	MNodeMessage::addAttributeChangedCallback( obj, geometryChangedCallback, (void *)itemFound); // Add callback for attr changed.
		itemFound->m_objectChangeMonitor = 0;
		itemFound->m_objectChangeMonitor = 
			MNodeMessage::addNodeDirtyCallback( obj, geometryDirtyCallback, (void *)itemFound); // Add callback for node changed.		
		m_geometryItemList.push_back( itemFound );
	}
	else
	{
		Geometry = itemFound->m_objectGeometry;
	}

	// Create a new set of buffers for this mesh
	//
	return Geometry;
}


//
// Get the DirectX texture for a Maya texture node
//
D3DTexture* D3DResourceManager::getTexture( MObject& textureNode)
{
	D3DTexture* Texture = NULL;

	// Look for a cached texture ...
	//
	// Check to see if object is in the list, if not added a
	// new item and cache some texture
	TextureItem *itemFound = NULL;

	TextureItemList::const_iterator it, end_it;
	end_it = m_textureItemList.end();
	for (it = m_textureItemList.begin(); it != end_it;  it++)
	{
		TextureItem *item = *it;
		if (item && item->m_mayaNode.object() == textureNode)
		{
			itemFound = item;
		}
	}
	// Build a new item, and add it to the list
	if (!itemFound)
	{
		itemFound = new TextureItem();
		itemFound->m_mayaNode = textureNode;
		Texture = itemFound->m_texture = new D3DTexture( textureNode);

		itemFound->m_objectDeleteMonitor = 
			MNodeMessage::addNodeAboutToDeleteCallback( textureNode, textureDeleteCallback, (void *)itemFound ); // Add callback for node deleted.
		itemFound->m_objectChangeMonitor = 
			MNodeMessage::addAttributeChangedCallback( textureNode, textureChangedCallback, (void *)itemFound); // Add callback for attr changed.

		m_textureItemList.push_back( itemFound );
	}
	else
	{
		Texture = itemFound->m_texture;
	}

	// Create a new set of buffers for this mesh
	//
	return Texture;
}

bool					
D3DResourceManager::initializeDefaultSurfaceEffect( const MString &effectsLocation, LPDIRECT3DDEVICE9 D3D,
												    const MString & effectName )
//
// Description:
//		Initialize default surface effects found in a given directory.
//
{
	HRESULT hres;
	LPD3DXBUFFER pBufferErrors = NULL;

	// Shader flags for debugging
    DWORD dwShaderFlags = 0;
    #ifdef DEBUG_VS
	dwShaderFlags |= D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT;
    #endif
    #ifdef DEBUG_PS
    dwShaderFlags |= D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT;
    #endif

	ID3DXEffect* effect = NULL;

	MString effectLocation = effectsLocation + "\\" + effectName + ".fx";
	hres = D3DXCreateEffectFromFile( D3D, 
		effectLocation.asChar(),
		NULL, 
		NULL, 
		dwShaderFlags, 
		NULL, 
		&effect, 
		&pBufferErrors );
	if (FAILED(hres))
	{
		MGlobal::displayInfo("Failed to compile FX file: " + effectLocation);
		if (pBufferErrors)
		{
			const char *pCompilErrors = (const char*)pBufferErrors->GetBufferPointer();
			MGlobal::displayInfo(MString("Compiler errors:\n") + MString(pCompilErrors) );
		}
	}	
	else
	{
		// Create a new effect item
		//
		MGlobal::displayInfo("Maya default pixel shader loaded: " + effectLocation);
		SurfaceEffectItem *pei = new SurfaceEffectItem;
		if (pei)
		{
			pei->fEffect = effect;
			pei->fName = effectName;

			m_SurfaceEffectItemList.push_back( pei );
		}
	}

	return (m_SurfaceEffectItemList.size() > 0);
}


bool					
D3DResourceManager::initializePostEffects( const MString &effectsLocation, LPDIRECT3DDEVICE9 D3D)
//
// Description:
//		Initialize all effects found in a given directory.
//
{
	HRESULT hres;
	LPD3DXBUFFER pBufferErrors = NULL;

	// Shader flags for debugging
    DWORD dwShaderFlags = 0;
    #ifdef DEBUG_VS
	dwShaderFlags |= D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT;
    #endif
    #ifdef DEBUG_PS
    dwShaderFlags |= D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT;
    #endif

	// Should do a proper directory search for now it's just hard coded.
	MStringArray effectNames;
	effectNames.append("PostProcess_SobelFilter");
	effectNames.append("PostProcess_ToneMapFilter");
	effectNames.append("PostProcess_InvertFilter");

	for (unsigned int i=0; i<effectNames.length(); i++)
	{
		ID3DXEffect* effect = NULL;

		MString effectLocation = effectsLocation + "\\" + effectNames[i] + ".fx";
		hres = D3DXCreateEffectFromFile( D3D, 
		                           effectLocation.asChar(),
		                           NULL, 
		                           NULL, 
		                           dwShaderFlags, 
		                           NULL, 
		                           &effect, 
		                           &pBufferErrors );
		if (FAILED(hres))
		{
			MGlobal::displayInfo("Failed to compiler FX file" + effectLocation);
			if (pBufferErrors)
			{
				const char *pCompilErrors = (const char*)pBufferErrors->GetBufferPointer();
				MGlobal::displayInfo(MString("Compiler errors:\n") + MString(pCompilErrors) );
			}
		}	
		else
		{
			// Create a new effect item
			//
			PostEffectItem *pei = new PostEffectItem;
			if (pei)
			{
				pei->fEffect = effect;
				pei->fName = effectNames[i];

				m_PostEffectItemList.push_back( pei );
			}
		}
	}

	return (m_PostEffectItemList.size() > 0);
}
		

const MStringArray &	
D3DResourceManager::getListOfEnabledPostEffects()
{
	// Find out which ones are enabled...
	// Should get via some UI interface. For now use Maya optionVars.
	//
	// e.g. optionVar -sv "D3D_EFFECTS_LIST" "PostProcess_ToneMapFilter" ;
	// e.g. optionVar -sv "D3D_EFFECTS_LIST" "PostProcess_ToneMapFilter,PostProcess_SobelFilter" ;
	// e.g. optionVar -sv "D3D_EFFECTS_LIST" "" ; // Clear the list
	m_EnabledPostEffectItemList.clear();

	const MString effectListVar("D3D_EFFECTS_LIST");
	//MString effectList("PostProcess_ToneMapFilter, PostProcess_SobelFilter");
	MString effectList;
	if (!MGlobal::getOptionVarValue(effectListVar, effectList))
	{
		// Create an option var if none previously existed.
		MGlobal::setOptionVarValue(effectListVar, effectList);
	}
	else
	{
		// Parse the effect list to get back a list of enabled effects
		// (in order). Note that there is not restriction on effect
		// list order, and duplicates are allowed.
		effectList.split(' ', m_EnabledPostEffectItemList);
	}

	return m_EnabledPostEffectItemList;
}

//////////////////////////////////////////////////////////
void					
D3DResourceManager::clearResources(bool onlyInvalidItems, bool clearShaders)
{
	GeometryItemList::const_iterator git, end_git;
	end_git = m_geometryItemList.end();
	for (git = m_geometryItemList.begin(); git != end_git;  git++)
	{
		GeometryItem *item = *git;
		if (item)
		{
			if (!onlyInvalidItems || (onlyInvalidItems && !(item->m_objectPath.isValid() )))
			{
				if (item->m_objectGeometry)
				{
					delete item->m_objectGeometry;
					item->m_objectGeometry = NULL;
				}
				item->m_objectPath = MDagPath(); // Assign invalid dag path

				// Kill the delete monitor
				if (item->m_objectDeleteMonitor)
				{
					MMessage::removeCallback( item->m_objectDeleteMonitor );
					item->m_objectDeleteMonitor = 0;
				}
				// Kill the attr changed monitor
				if (item->m_objectChangeMonitor)
				{
					MMessage::removeCallback( item->m_objectChangeMonitor );	
					item->m_objectChangeMonitor = 0;					
				}
				// Kill node dirty monitor
				if (item->m_objectDirtyMonitor)
				{
					MMessage::removeCallback( item->m_objectDirtyMonitor );	
					item->m_objectDirtyMonitor = 0;										
				}
			}
		}
	}
	if (!onlyInvalidItems)
		m_geometryItemList.clear();

	TextureItemList::const_iterator tit, end_tit;
	end_tit = m_textureItemList.end();
	for (tit = m_textureItemList.begin(); tit != end_tit;  tit++)
	{
		TextureItem *item = *tit;
		if (item)
		{
			if (!onlyInvalidItems || (onlyInvalidItems && !(item->m_mayaNode.isValid() )))
			{
				if (item->m_texture)
				{
					delete item->m_texture;
					item->m_texture = NULL;
				}

				item->m_mayaNode = MObject::kNullObj;

				// Kill the delete monitor
				if (item->m_objectDeleteMonitor)
				{
					MMessage::removeCallback( item->m_objectDeleteMonitor );
					item->m_objectDeleteMonitor = 0;
				}
				// Kill the attr changed monitor
				if (item->m_objectChangeMonitor)
				{
					MMessage::removeCallback( item->m_objectChangeMonitor );	
					item->m_objectChangeMonitor = 0;					
				}
			}
		}
	}
	if (!onlyInvalidItems)
		m_textureItemList.clear();

	if (clearShaders)
	{
		// Clean up post effects list
		{
		PostEffectItemList::const_iterator eit, end_eit;
		end_eit = m_PostEffectItemList.end();
		for (eit = m_PostEffectItemList.begin(); eit != end_eit;  eit++)
		{
			PostEffectItem *item = *eit;
			if (item)
			{
				if (item->fEffect)
				{
					item->fEffect->Release();
					item->fEffect = NULL;
				}			
				delete item;
			}
		}
		m_PostEffectItemList.clear();
		m_EnabledPostEffectItemList.clear();
	}

		// Clean up surface effects list
		{
			SurfaceEffectItemList::const_iterator eit, end_eit;
			end_eit = m_SurfaceEffectItemList.end();
			for (eit = m_SurfaceEffectItemList.begin(); eit != end_eit;  eit++)
			{
				SurfaceEffectItem *item = *eit;
				if (item)
				{
					if (item->fEffect)
					{
						item->fEffect->Release();
						item->fEffect = NULL;
					}			
					delete item;
				}
			}
			m_SurfaceEffectItemList.clear();
		}
	}
}


#endif /* D3D9_SUPPORTED */



